package com.smartrm.trade.domain;

import com.google.common.collect.Maps;
import com.smartrm.globalinfrcore.aggreate.BaseAggregate;
import com.smartrm.globalinfrcore.api.CommonError;
import com.smartrm.globalinfrcore.api.ErrorCode;
import com.smartrm.globalinfrcore.event.DomainEventBus;
import com.smartrm.globalinfrcore.exception.DomainException;
import com.smartrm.globalinfrcore.idgenerator.UniqueIdGeneratorUtil;
import com.smartrm.trade.domain.event.OrderCreatedEvent;
import com.smartrm.trade.domain.remote.*;
import com.smartrm.trade.domain.valueobject.PaymentQrCodeVOJ;
import com.smartrm.trade.domain.valueobject.PlatformType;
import com.smartrm.trade.domain.valueobject.StockedCommodityVOJ;
import com.smartrm.trade.domain.valueobject.VendingMachineCommodityListVOJ;
import com.smartrm.trade.infrasturcture.TradeError;
import com.sun.deploy.net.DownloadException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/*
 * @program: common-starters
 * @description: 货道售卖机实体，也是聚合根，1：直接面向用户，2：生命周期最1
 * 使用事件风暴法在具体的交易上下文中的-货道售卖机扫码支付业务,找到具体的实体
 * ,值对象，领域事件，领域服务和聚合根。指导代码编写
 * @author: houhong
 * @create: 2023-01-10 15:41
 */
public class SlotVendingMachineDO extends BaseAggregate {

    private static final Logger logger = LoggerFactory.getLogger(SlotVendingMachineDO.class);

    /*
     * 实体有唯一表识
     */
    private long machineId;
    /*
     * 实体状态,对于同一个货道机而言，不同的线程进来，应该是当前货道售卖机状态应该线程共享
     */
    public static volatile SlotVendingMachineState state;
    /*
     * 当前订单
     */
    public OrderDO curOrder;


    /*
     * 只能通过仓储生成
     */
    private SlotVendingMachineDO() {
    }


    public long getMachineId() {
        return machineId;
    }

    public SlotVendingMachineState getState() {
        return state;
    }

    public OrderDO getCurOrder() {
        return curOrder;
    }

    /*
     * 业务:顾客通过货道售卖机查看商品列表 <--->
     * 实体行为：货道售卖机展示商品.
     */
    public VendingMachineCommodityListVOJ getCommodityList(TradeDeviceService tradeDeviceService
            , TradeCommodityService commodityService) {

        // 当前货道售卖机的库存数量
        List<InventoryInfo> inventoryInfos = tradeDeviceService.getInventory(machineId);
        List<String> commodityInfos = inventoryInfos.stream().map(inventoryInfo -> inventoryInfo.getCommodityId())
                .collect(Collectors.toList());

        List<CommodityInfo> commodityList = commodityService.getCommodityList(commodityInfos);
        Map<String, Integer> inventoryMap = Maps.newConcurrentMap();

        inventoryInfos.forEach(inventoryInfo -> inventoryMap.put(inventoryInfo.getCommodityId(), inventoryInfo.getCount()));

        List<StockedCommodityVOJ> commodities = commodityList.stream().map(info ->
                new StockedCommodityVOJ(
                        info.getCommodityId(),
                        info.getCommodityName(),
                        info.getImageUrl(),
                        info.getPrice(),
                        inventoryMap.get(info.getCommodityId())
                )
        ).collect(Collectors.toList());

        return new VendingMachineCommodityListVOJ(machineId, commodities);
    }

    /*
     * 业务：货道售卖机在展示了商品列表后，选择商品，生成订单，生成支付二维码
     * 实体行为：根据业务分析，这里可以为货道售卖机的实体行为
     */
    public PaymentQrCodeVOJ selectCommodity(Collection<StockedCommodityVOJ> commodities,
                                            TradeDeviceService deviceService, TradePayService payService,
                                            PlatformType platformType) throws Exception {
        //校验货道售卖机道状态
        if (state != SlotVendingMachineState.Ready) {
            throw new DomainException(TradeError.VendingMachineStateNotRight);
        }
        //校验库存
        if (!checkInventory(commodities, deviceService)) {
            throw new DomainException(TradeError.InventoryCheckFail);
        }
        //生成订单
        curOrder = this.generateOrder(commodities);
        //发布领域事件-- 订单已创建
        emitEvent(new OrderCreatedEvent(this.machineId, curOrder));
        //修改当前货道售卖机状态
        state = SlotVendingMachineState.Trading;
        PaymentQrCodeVOJ ret = payService.starQrCodePayforOrder(platformType, curOrder);
        curOrder.setPaymentId(ret.getPaymentId());
        //实现乐观锁
        incVersion();
        return ret;
    }

    /*
     * 检查
     */
    public Boolean checkInventory(Collection<StockedCommodityVOJ> commodities,
                                  TradeDeviceService deviceService) {
        //获取当前售卖机的库存
        List<InventoryInfo> inventoryInfos = deviceService.getInventory(machineId);
        Map<String, Integer> inventoryMap = Maps.newHashMap();
        inventoryInfos.forEach(info -> inventoryMap.put(info.getCommodityId(), info.getCount()));

        List<StockedCommodityVOJ> countError = commodities.stream().filter(stock ->
                stock.getCount() == null || inventoryMap.get(stock.getCommodityId()) < stock.getCount())
                .collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(countError)) {
            return false;
        }
        return true;
    }


    /*
     * 货道售卖机生成订单
     */
    public OrderDO generateOrder(Collection<StockedCommodityVOJ> commodityVOJS) {

        return OrderDO.Builder().commodities(commodityVOJS)
                .eventBus(domainEventBus)
                .machineId(machineId)
                .type(OrderType.SlotQrScanePaid)
                .state(OrderState.Start)
                .orderId(UniqueIdGeneratorUtil.instance().nextId())
                .build();

    }


    public static Builder Builder() {
        return new Builder();
    }

    public static class Builder {

        private long machineId;

        private SlotVendingMachineState state;

        private OrderDO curOrder;

        private long version;

        private DomainEventBus eventBus;

        public Builder machineId(long machineId) {
            this.machineId = machineId;
            return this;
        }

        public Builder state(SlotVendingMachineState state) {
            this.state = state;
            return this;
        }

        public Builder curOrder(OrderDO curOrder) {
            this.curOrder = curOrder;
            return this;
        }

        public Builder eventBus(DomainEventBus eventBus) {
            this.eventBus = eventBus;
            return this;
        }

        public Builder version(long version) {
            this.version = version;
            return this;
        }

        public SlotVendingMachineDO build() {
            if (this.eventBus == null) {
                throw new DomainException(CommonError.UnExpected);
            }
            SlotVendingMachineDO machine = new SlotVendingMachineDO();
            machine.curOrder = this.curOrder;
            machine.machineId = this.machineId;
            machine.state = this.state;
            machine.setEventBus(this.eventBus);
            machine.version = this.version;
            return machine;
        }

    }


}